/*设备描述文件放到设备树中去了，因此不再需要一个专门的设备文件*/
/*
1：申请设备号方便系统识别和管理设备，同时在/dev目录下创建文件
2: 创建cdev会在内核中创建一个新的 cdev 结构体，并将其与指定的设备号和数量关联。它还会在 /proc/devices 和 /sys/class 目录中创建相应的文件。
3：创建class会在 sysfs 中创建一个新的目录，用于存放与设备相关的文件
4：创建设备会设备类中创建一个新的设备节点，并将其与指定的设备号关联。
*/


#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

struct led {
    dev_t devid;                        /* 设备号 */
    struct cdev cdev;
    struct class * class;
    struct device * device;
    struct device_node * nd;           /*   设备树节点  */
    int rgb_led_red;                   /*   通过名字获取到的引脚编号    */
};


struct led gpio_led;


/*of_device_id 用于device和driver的match*/
struct of_device_id led[]={
    {.compatible = "RGB"},
    {}
};

static ssize_t tree_driver_write(struct file * file, const char __user * buf, size_t count, loff_t * lofft)
{
    return 0;
}
static int tree_driver_open(struct inode * inode, struct file * file)
{
    return 0;
}



/*提供文件接口*/
const struct file_operations tree_driver_fops ={
    .owner = THIS_MODULE,
    .open = tree_driver_open,
    .write = tree_driver_write,
};

/*平台驱动部分代码*/


int tree_driver_probe(struct platform_device * pdev)
{
     printk(KERN_EMERG"\r\n hello world !\r\n");
    /*先把字符设备注册出来*/
    /*第一个数字是从设备号，第二个数字是设备个数*/
    alloc_chrdev_region(& gpio_led.devid,0,1,"gpio_led");  /*申请设备号*/
    /*创建cdev*/
    gpio_led.cdev.owner = THIS_MODULE;
    cdev_init(&gpio_led.cdev,&tree_driver_fops);
    cdev_add(&gpio_led.cdev,gpio_led.devid,1);  /*bug就是在这个地方，这里的数字是有要求的*/
    /*创建class*/
    gpio_led.class = class_create(THIS_MODULE,"gpio_led");
    /*创建设备*/
    gpio_led.device = device_create(gpio_led.class,NULL,gpio_led.devid,NULL,"gpioled");

    /*从设备树中获取设备数据*/
    gpio_led.nd = of_find_node_by_path("/RGB");
    gpio_led.rgb_led_red = of_get_named_gpio(gpio_led.nd,"rgb_led_red",0);

    /*申请使用gpio*/
    gpio_request(gpio_led.rgb_led_red,NULL);

    gpio_direction_output(gpio_led.rgb_led_red,0);
    return 0;
}



int tree_driver_remove(struct platform_device * pdev)
{
    /*后创建的先注销*/
    device_destroy(gpio_led.class,gpio_led.devid);
    class_destroy(gpio_led.class);
    cdev_del(&gpio_led.cdev);
    /*第二个参数是释放的设备个数*/
    unregister_chrdev_region(gpio_led.devid,1);
    return 0;
}

struct platform_driver tree_driver={
    .probe= tree_driver_probe,
    .remove= tree_driver_remove,
    .driver = {
        .name = "led_tree_platform",
        .owner = THIS_MODULE,
        .of_match_table = led,
    },
};

/*这个函数只负责平台驱动的创建*/
static __init int tree_driver_init(void)
{
    platform_driver_register(&tree_driver);
    return 0;
}

static __exit void tree_driver_exit(void)
{
    platform_driver_unregister(&tree_driver);
}

/*驱动入口和出口*/
module_init(tree_driver_init);
module_exit(tree_driver_exit);

/*驱动描述*/
MODULE_AUTHOR("hanyang");
MODULE_LICENSE("GPL");

